package com.lean.android.ad.core

import android.app.Activity
import android.app.Application
import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.bytedance.sdk.openadsdk.TTAdConfig
import com.bytedance.sdk.openadsdk.TTAdConstant
import com.bytedance.sdk.openadsdk.TTAdSdk
import com.bytedance.sdk.openadsdk.mediation.init.MediationConfig
import com.bytedance.sdk.openadsdk.mediation.init.MediationConfigUserInfoForSegment
import com.lean.android.ad.core.config.AdConfig
import com.lean.android.ad.core.splash.SplashAdManager
import com.lean.android.ad.duration.DurationManager
import com.lean.android.ad.repository.ReportHelper
import com.lean.android.ad.repository.Repositories
import com.lean.android.ad.repository.localcache.AdLocalCache
import com.lean.android.ad.repository.localcache.AdSwitchCache
import com.lean.android.ad.repository.model.AdSwitchInfo
import com.lean.android.ad.utils.AdLog
import com.lean.android.ad.utils.NetworkUtils
import com.lean.android.ad.utils.UmengHelper
import com.lean.android.network.Result
import com.lean.android.sysinfo.CpuDataNativeProvider
import com.umeng.commonsdk.UMConfigure
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

object AdSdk {

    private val appScope by lazy {
        ProcessLifecycleOwner.get().lifecycleScope
    }

    val splashAdManager: SplashAdManager by lazy {
        SplashAdManager()
    }

    var debug: Boolean = false

    var isVip: Boolean = false //会员免广告

    private lateinit var _adConfig: AdConfig

    private var isInitializing = false

    lateinit var appContext: Context

    val adInitState = MutableStateFlow<AdInitState>(AdInitState.Idle)

    fun init(app: Application, adConfig: AdConfig) {
        appContext = app.applicationContext
        //init oaid
        System.loadLibrary("msaoaidsec")
        //init cpuinfo
        System.loadLibrary("cpuinfo-libs")
        CpuDataNativeProvider.initLibrary()
        //init log
        AdLog.init()

        AdLog.d(AdLog.AdInitTag, "AdSdk init start.")
        _adConfig = adConfig

        //init duration.
        initDurationManager(app)
        //init local cache
        AdLocalCache.init()
        //init network
        AdLog.d(AdLog.AdInitTag, "init NetworkUtils.")
        NetworkUtils.init(app.applicationContext)

        //在广告配置获取成功，且用户同意的情况下，开始初始化GroMore
        awaitForInitGroMore(app, adConfig)

        splashAdManager.registerActivityCallbacks(app)

        appScope.launch {
            adInitState.collectLatest {
                when (it) {
                    is AdInitState.Success -> {
                        //广告初始化成功
                        AdLog.d(AdLog.AdInitTag, "AdSdk init success. ")
                        splashAdManager.bindToLifecycle() //splashAd开始监听activity生命周期
                    }

                    is AdInitState.Error -> {
                        AdLog.e(AdLog.AdInitTag, it.message)
                    }

                    else -> {

                    }
                }
            }
        }
    }

    private fun initDurationManager(app: Application) {
        DurationManager.init(app)
        appScope.launch {
            DurationManager.getAppDurationFlow().collectLatest { duration ->
                if (duration > 0) {
                    AdLog.d(AdLog.AdInitTag, "duration changed to $duration , start to report.")
                    ReportHelper.reportAppDuration(duration)
                }
            }
        }
    }

    private suspend fun getOaidFromUM(context: Context): String? {
        return suspendCoroutine { coroutine ->
            AdLog.d(AdLog.AdInitTag, "start to load oaid from UM.")
            val startTime = System.currentTimeMillis()
            //oaid.
            if (AdLocalCache.oaid.isEmpty()) {
                UMConfigure.getOaid(context) { oaid ->
                    if (!oaid.isNullOrEmpty()) {
                        AdLocalCache.oaid = oaid
                        AdLog.d(
                            AdLog.AdInitTag,
                            "load oaid success from UM, value : $oaid, cost ${System.currentTimeMillis() - startTime}"
                        )
                    }
                    coroutine.resume(oaid)
                }
            } else {
                AdLog.d(
                    AdLog.AdInitTag,
                    "local oaid exist , value : ${AdLocalCache.oaid}, cost ${System.currentTimeMillis() - startTime}"
                )
                coroutine.resume(AdLocalCache.oaid)
            }
        }
    }

    private suspend fun getOaidFromSdk(context: Context, accessPem: String): String? {
        return suspendCoroutine { coroutine ->
            AdLog.d(AdLog.AdInitTag, "start to load oaid from SDK.")
            val startTime = System.currentTimeMillis()
            //oaid.
            if (AdLocalCache.oaid.isEmpty()) {
                val oaidHelper = OaidHelper { oaid ->
                    if (!oaid.isNullOrEmpty()) {
                        AdLocalCache.oaid = oaid
                        AdLog.d(
                            AdLog.AdInitTag,
                            "load oaid success from SDK, value : $oaid, cost ${System.currentTimeMillis() - startTime}"
                        )
                    }
                    coroutine.resume(oaid)
                }
                oaidHelper.getDeviceIds(context, accessPem)
            } else {
                AdLog.d(
                    AdLog.AdInitTag,
                    "local oaid exist , value : ${AdLocalCache.oaid}, cost ${System.currentTimeMillis() - startTime}"
                )
                coroutine.resume(AdLocalCache.oaid)
            }
        }
    }

    private suspend fun getOaid(context: Context, accessPem: String): String? {
        return getOaidFromUM(context) ?: getOaidFromSdk(context, accessPem)
    }

    private fun adConfigFlow(): Flow<Result<AdSwitchInfo>> {
        return Repositories.adRepository.loadAdInfo()
    }

    private fun awaitForInitGroMore(context: Context, adConfig: AdConfig) {
        appScope.launch {
            combine(
                adConfigFlow(), AdLocalCache.privacyAgreeFlow
            ) { switchResult, agree ->
                agree to switchResult
            }.collectLatest { (agree, result) ->
                if (result is Result.Success) {
                    AdLog.d(
                        AdLog.AdInitTag, "ad config load success, privacy agreed $agree "
                    )
                    if (agree) {
                        doInitial(context, adConfig)
                    } else {
                        if (AdSwitchCache.isCompliance) {
                            adInitState.value = AdInitState.BlockedByPrivacyDialog
                        } else {
                            doInitial(context, adConfig)
                        }
                    }
                }
            }
        }
    }

    private fun doInitial(context: Context, adConfig: AdConfig) {
        if (isSdkInit()) return
        if (isInitializing) return
        isInitializing = true
        appScope.launch {
            getOaid(context, adConfig.accessPem) //获得oaid并缓存
        }
        //预配置
        AdLog.d(AdLog.AdInitTag, "preInitGroMore.")
        preInitGroMore(context, adConfig)

        AdLog.d(AdLog.AdInitTag, "start init sdk with config $adConfig.")
        startInitGroMore()

        UmengHelper.init(context)
    }

    fun isSdkInit(): Boolean {
        return adInitState.value == AdInitState.Success && TTAdSdk.isSdkReady()
    }

    fun getAdConfig(): AdConfig {
        if (!::_adConfig.isInitialized) {
            throw IllegalAccessException("you can not access adConfig before init")
        }

        return _adConfig
    }

    private fun preInitGroMore(context: Context, adConfig: AdConfig) {
        // 设置流量分组的信息
        val userInfoMap = MediationConfigUserInfoForSegment()
        userInfoMap.userId = adConfig.userIdProvider.getUserId()
        userInfoMap.channel = adConfig.channelInfoProvider.getChannel()
        // 接入网盟广告sdk的初始化操作，详情见接入文档和穿山甲平台说明
        TTAdSdk.init(
            context,
            TTAdConfig.Builder().appId(adConfig.adParamsProvider.getPangolinId())
                .appName(adConfig.appInfoProvider.getAppName()) //必填，不能为空
                .debug(debug) //测试阶段打开，可以通过日志排查问题，上线时去除该调用
                .useMediation(true).directDownloadNetworkType(
                    TTAdConstant.NETWORK_STATE_2G,
                    TTAdConstant.NETWORK_STATE_3G,
                    TTAdConstant.NETWORK_STATE_4G,
                    TTAdConstant.NETWORK_STATE_WIFI,
                    TTAdConstant.NETWORK_STATE_MOBILE,
                    TTAdConstant.NETWORK_STATE_5G
                ).useTextureView(true).paid(false)  //是否为计费用户
                .titleBarTheme(TTAdConstant.TITLE_BAR_THEME_DARK) //(穿山甲SDK相关配置)
                .allowShowNotify(true).setMediationConfig(
                    MediationConfig.Builder().setMediationConfigUserInfoForSegment(userInfoMap)
                        .setOpenAdnTest(debug) //开启第三方ADN测试时需要设置为true，会每次重新拉去最新配置，release 包情况下必须关闭.默认false
                        .setPublisherDid(AdLocalCache.deviceId) //用户自定义device_id
                        .setHttps(true).build()
                ).build()
        )
    }

    private fun startInitGroMore() {
        if (isSdkInit()) return
        AdLog.d(AdLog.AdInitTag, "startInitGroMore.")
        TTAdSdk.start(object : TTAdSdk.Callback {
            override fun success() {
                AdLog.d(AdLog.AdInitTag, "gromore init success.")
                adInitState.value = AdInitState.Success
                isInitializing = false
            }

            override fun fail(code: Int, message: String?) {
                AdLog.d(AdLog.AdInitTag, "gromore init failed.")
                adInitState.value = AdInitState.Error(code, message)
                isInitializing = false
            }
        })
    }

    fun showAd(): Boolean {
        return !isVip && AdSwitchCache.adSwitch
    }

    fun isBlack(): Boolean {
        return false
    }

    fun getTopActivity(): Activity? = splashAdManager.getTopActivity()

    sealed interface AdInitState {
        data object Idle : AdInitState

        data object BlockedByPrivacyDialog : AdInitState

        data object Success : AdInitState

        data class Error(val code: Int, val message: String?) : AdInitState
    }

}